package com.miyake.dsp.osa.lab.ui;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.border.TitledBorder;

import com.miyake.dsp.filter.FirScilab;
import com.miyake.dsp.filter.IirScilab;
import com.miyake.dsp.osa.lab.ChartSettingEx;
import com.miyake.dsp.osa.lab.ChartSettingEx.YAxisType;
import com.miyake.dsp.osa.lab.Connector;
import com.miyake.dsp.osa.lab.LabContainer;
import com.miyake.dsp.osa.lab.LabFactory;
import com.miyake.dsp.osa.lab.PersistentManager;
import com.miyake.dsp.osa.lab.SettingPanelListener;
import com.miyake.dsp.osa.lab.wrapper.ChartExWrapper;
import com.miyake.dsp.osa.lab.wrapper.ChartWrapper;
import com.miyake.dsp.osa.lab.wrapper.Ms9740aCsvWrapper;
import com.miyake.dsp.osa.lab.wrapper.NoiseDetectorWrapper;
import com.miyake.dsp.osa.lab.wrapper.OtdrSimulatorRxWrapper;
import com.miyake.dsp.osa.lab.wrapper.OtdrSimulatorTxWrapper;
import com.miyake.dsp.otdr.OtdrImpulseResponseSetting;
import com.miyake.dsp.otdr.OtdrSetting;

/**
 * @author a1199022
 *
 */
public class MainLabUI extends JFrame {

	public List<List<LabContainer>> getColumns() {
		return columns;
	}

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	protected List<List<LabContainer>> columns = new ArrayList<List<LabContainer>>();
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		new MainLabUI().setVisible(true);
	}	
	
	private WirePainter wirePainter;
	private LabFactory factory;
	private JPanel mainPanel;
	private JScrollPane scrollPane;
	private UiPanelListener uiPanelListener;
	private JPopupMenu popup;
	private MouseListener mouseListener;
	private Controller controller;
	
	public MainLabUI() {
		setSize(new Dimension(800,600));
		this.setTitle("LOVEView");
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.addWindowListener(new WindowListener() {

			@Override
			public void windowActivated(WindowEvent arg0) {
				// TODO Auto-generated method stub
			}

			@Override
			public void windowClosed(WindowEvent arg0) {
				PersistentManager.getInstance().save("test.xml", columns);
			}

			@Override
			public void windowClosing(WindowEvent arg0) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void windowDeactivated(WindowEvent arg0) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void windowDeiconified(WindowEvent arg0) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void windowIconified(WindowEvent arg0) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void windowOpened(WindowEvent arg0) {
				// TODO Auto-generated method stub
				
			}
			
		});
		// Does not save to file
		PersistentManager.getInstance().addOmitField(OtdrImpulseResponseSetting.class, "entryPoint");
		PersistentManager.getInstance().addOmitField(OtdrSetting.class, "entryPoint");
		PersistentManager.getInstance().addOmitField(IirScilab.class, "source");
		PersistentManager.getInstance().addOmitField(FirScilab.class, "source");
		
		mouseListener = new MouseListener() {
			@Override
			public void mouseClicked(MouseEvent arg0) {
				if(javax.swing.SwingUtilities.isRightMouseButton(arg0)){
					showPopup(arg0.getLocationOnScreen(), (JPanel)arg0.getSource());
				}
				else {
					hidePopup();
				}
			}

			@Override
			public void mouseEntered(MouseEvent arg0) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void mouseExited(MouseEvent arg0) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void mousePressed(MouseEvent arg0) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void mouseReleased(MouseEvent arg0) {
				// TODO Auto-generated method stub
				
			}
			
		};
		this.addMouseListener(mouseListener);
		
		uiPanelListener = new UiPanelListener(){

			@Override
			public void onDragging(int x, int y) {
				wirePainter.setDragEnd(new Point(x, y));
				doScroll(new Point(x, y));
				repaint();
			}

			@Override
			public void onFinishDragging(int x, int y, LabContainer container, String key) {
				wirePainter.setLastConnectedContainer(container);
				wirePainter.setLastConnectedConnectorKey(key);
				wirePainter.setConnectorComponent(null);
				wirePainter.startWavingDraw();
			}

			@Override
			public void onRemove(CalculatorUiPanel panel) {
				removeUiPanel(panel);
			}

			@Override
			public void onStartChanging(int x, int y, Connector connector, Component component) {
				LabContainer c = connector.getParent();
				Component b = c.getUI().getConnectorComponent(connector.getKey());
				wirePainter.setConnectorComponent(b);
				wirePainter.setDragStart(c.getUI().getPoint(connector.getKey())/*c.getUI().getConnectorLocaltion(connector)*/);
			}

			@Override
			public void onInConnectorRemove(String key) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void onOutConnectorRemove(String key) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void onStartDragging(int x, int y, Component component) {		
				wirePainter.setConnectorComponent(component);
				wirePainter.setDragStart(new Point(x, y));
				wirePainter.setDragEnd(new Point(x, y));
			}

			
		};
	
		createController();
		
		mainPanel = new JPanel();
		mainPanel.setName("MainPanel");
		//mainPanel.setLayout(new GridLayout());
		mainPanel.setLayout(new FlowLayout(FlowLayout.LEFT));

		mainPanel.addMouseListener(mouseListener);
		
		scrollPane = new JScrollPane(mainPanel) {
			/**
			 * 
			 */
			private static final long serialVersionUID = 1L;

			@Override
			public void paint(Graphics arg0) {
				super.paint(arg0);
				arg0.setClip(scrollPane.getX(), scrollPane.getY(), 
						scrollPane.getWidth() - scrollPane.getVerticalScrollBar().getWidth() - 1, 
						scrollPane.getHeight() - scrollPane.getHorizontalScrollBar().getHeight() - 1);
				
				drawDraggedContainer(arg0);
				//drawAllWires((Graphics2D)arg0);
				wirePainter.draw((Graphics2D)arg0);
			}
		};
		getContentPane().add(scrollPane);
		
		scrollPane.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() {
			@Override
			public void adjustmentValueChanged(AdjustmentEvent arg0) {
				scrollPane.repaint();
			}
		});
		scrollPane.getHorizontalScrollBar().addAdjustmentListener(new AdjustmentListener() {
			@Override
			public void adjustmentValueChanged(AdjustmentEvent arg0) {
				scrollPane.repaint();
			}
		});
		
		wirePainter = new WirePainter(scrollPane) {
			@Override
			protected List<List<LabContainer>> getColumns() {
				return columns;
			}
			
		};
		factory = LabFactory.getInstance();

		columns = PersistentManager.getInstance().open("test.xml");
		if (columns == null) {
			columns = new ArrayList<List<LabContainer>>(); 
		}
		buildLayout(false);		
		
		//PersistentManager.getInstance().save("test.xml", columns);
	}

	protected void drawDraggedContainer(Graphics arg0) {
		if (!isLabContainerDragged()) {
			return;
		}
		
		arg0.drawRect(draggedContainerX, draggedContainerY, 
				draggedContainerUI.getWidth(), draggedContainerUI.getHeight());
	}

	private boolean isLabContainerDragged() {
		return this.draggedContainerUI != null;
	}

	protected void removeLabContainer(LabContainer container) {
		// Remove connectors
		for (String key : container.getInConnectors().keySet()) {
			container.removeInConnector(key);
		}
		for (String key : container.getOutConnectors().keySet()) {
			container.removeOutConnector(key);
		}
		
		// Remove itself
		for (List<LabContainer> list : columns) {
			if (list.contains(container)) {
				list.remove(container);
				if (list.size() == 0) {
					columns.remove(list);
				}
				break;
			}

		}
	}
	protected void removeUiPanel(CalculatorUiPanel panel) {
		removeLabContainer(panel.getContainer());
		this.buildLayout(true);
	}

	protected void hidePopup() {
		if (popup != null) {
			popup.setVisible(false);
//			mainPanel.remove(popup);
		}
	}
	
	
	int number = 0;
	protected CalculatorUiPanel draggedContainerUI;
	protected int draggedContainerX;
	protected int draggedContainerY;
	protected int draggedContainerXoffset;
	protected int draggedContainerYoffset;
	protected JPanel partitionPanel;

	protected void showPopup(Point point, JPanel jPanel) {
		if (popup == null) {
			popup = new JPopupMenu(); 
			
			JMenu otdrMenu = MyPopupMenu.createSubMenu("OTDR");
			JMenu osaMenu = MyPopupMenu.createSubMenu("OSA");
			JMenu chartMenu = MyPopupMenu.createSubMenu("Chart");
			JMenu sourceMenu = MyPopupMenu.createSubMenu("Source");
			JMenu filterMenu = MyPopupMenu.createSubMenu("Filter");
			popup.add(otdrMenu);
			popup.add(osaMenu);
			popup.add(chartMenu);
			popup.add(sourceMenu);
			popup.add(filterMenu);
			otdrMenu.add(MyPopupMenu.createMenuItem("OTDR Simulator Package", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					createOtdrPackage();
					popup.setVisible(false);
				}
			}));
			
			osaMenu.add(MyPopupMenu.createMenuItem("OSA CSV Package", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					createOsaPackage();
					popup.setVisible(false);
				}
			}));
			osaMenu.add(MyPopupMenu.createMenuItem("MS9740A", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createMs9740a(), number);
					popup.setVisible(false);
				}
			}));	
			osaMenu.add(MyPopupMenu.createMenuItem("Hige Detector", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createHigeSearch(), number);
					popup.setVisible(false);
				}
			}));
			otdrMenu.add(MyPopupMenu.createMenuItem("OTDR Simulator Tx", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createOtdrTx(), number);
					popup.setVisible(false);
				}
			}));
			otdrMenu.add(MyPopupMenu.createMenuItem("OTDR Simulator Rx", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createOtdrRx(), number);
					popup.setVisible(false);
				}
			}));
			otdrMenu.add(MyPopupMenu.createMenuItem("OTDR Simulator Rx (All Range)", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createOtdrRxEx(), number);
					popup.setVisible(false);
				}
			}));
			otdrMenu.add(MyPopupMenu.createMenuItem("uOTDR", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createRealOtdr(), number);
					popup.setVisible(false);
				}
			}));
			otdrMenu.add(MyPopupMenu.createMenuItem("SR4731 Reader", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createSr4731Reader(), number);
					popup.setVisible(false);
				}
			}));
			sourceMenu.add(MyPopupMenu.createMenuItem("Pulse Generator", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createPulseGenerator(), number);
					popup.setVisible(false);
				}
			}));
			sourceMenu.add(MyPopupMenu.createMenuItem("FP-LD", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createFpLd(), number);
					popup.setVisible(false);
				}
			}));
			sourceMenu.add(MyPopupMenu.createMenuItem("Gauss Generator", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createGaussGenerator(), number);
					popup.setVisible(false);
				}
			}));
			filterMenu.add(MyPopupMenu.createMenuItem("Analogue", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createFilter(), number);
					popup.setVisible(false);
				}
			}));
			filterMenu.add(MyPopupMenu.createMenuItem("Edge Filter", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createEdgeFilter(), number);
					popup.setVisible(false);
				}
			}));
			filterMenu.add(MyPopupMenu.createMenuItem("Unsharp Mask", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createUnsharpMask(), number);
					popup.setVisible(false);
				}
			}));
			filterMenu.add(MyPopupMenu.createMenuItem("Moving Average", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createMovingAverage(), number);
					popup.setVisible(false);
				}
			}));
			filterMenu.add(MyPopupMenu.createMenuItem("Gaussian Filter", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createGaussianFilter(), number);
					popup.setVisible(false);
				}
			}));
			popup.add(MyPopupMenu.createMenuItem("Convolution", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createConvolution(), number);
					popup.setVisible(false);
				}
			}));
			filterMenu.add(MyPopupMenu.createMenuItem("Diff", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createDiff(), number);
					popup.setVisible(false);
				}
			}));
			filterMenu.add(MyPopupMenu.createMenuItem("Median Filter", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createMedianFilter(), number);
					popup.setVisible(false);
				}
			}));
			popup.add(MyPopupMenu.createMenuItem("Decibel", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createDecibel(), number);
					popup.setVisible(false);
				}
			}));
			popup.add(MyPopupMenu.createMenuItem("Noise Gate", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createNoiseGate(), number);
					popup.setVisible(false);
				}
			}));		
			popup.add(MyPopupMenu.createMenuItem("Power", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createPower(), number);
					popup.setVisible(false);
				}
			}));
			sourceMenu.add(MyPopupMenu.createMenuItem("Noise Generator", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createNoiseGenerator(), number);
					popup.setVisible(false);
				}
			}));
			osaMenu.add(MyPopupMenu.createMenuItem("MS9740A CSV", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createMs9740aCsv(), number);
					popup.setVisible(false);
				}
			}));
			popup.add(MyPopupMenu.createMenuItem("Text File", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createTextReader(), number);
					popup.setVisible(false);
				}
			}));
			popup.add(MyPopupMenu.createMenuItem("Peak Search", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createPeakSearch(), number);
					popup.setVisible(false);
				}
			}));
			popup.add(MyPopupMenu.createMenuItem("Peak Search2", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createPeakSearch2(), number);
					popup.setVisible(false);
				}
			}));
			chartMenu.add(MyPopupMenu.createMenuItem("Chart (4 Channels)", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createChart(), number);
					popup.setVisible(false);
				}
			}));
			chartMenu.add(MyPopupMenu.createMenuItem("Chart Ex (8 Channels)", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createChartEx(), number);
					popup.setVisible(false);
				}
			}));
			popup.add(MyPopupMenu.createMenuItem("Noise Detector", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createNoiseDetector(), number);
					popup.setVisible(false);
				}
			}));
			popup.add(MyPopupMenu.createMenuItem("Noise Detector2", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createNoiseDetector2(), number);
					popup.setVisible(false);
				}
			}));
			popup.add(MyPopupMenu.createMenuItem("Math", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createMath(), number);
					popup.setVisible(false);
				}
			}));

			popup.add(MyPopupMenu.createMenuItem("Sort", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createSort(), number);
					popup.setVisible(false);
				}
			}));
			popup.add(MyPopupMenu.createMenuItem("Extract by Y", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createExtractY(), number);
					popup.setVisible(false);
				}
			}));
			popup.add(MyPopupMenu.createMenuItem("Edge Detector", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createEdgeDetectorWrapper(), number);
					popup.setVisible(false);
				}
			}));
			popup.add(MyPopupMenu.createMenuItem("Limiter", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createLimiter(), number);
					popup.setVisible(false);
				}
			}));
			popup.add(MyPopupMenu.createMenuItem("Amplifier", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createAmplifier(), number);
					popup.setVisible(false);
				}
			}));
			otdrMenu.add(MyPopupMenu.createMenuItem("Auto Setting", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createAutoSetting(), number);
					popup.setVisible(false);
				}
			}));
			popup.add(MyPopupMenu.createMenuItem("FFT", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createFFT(), number);
					popup.setVisible(false);
				}
			}));
			popup.add(MyPopupMenu.createMenuItem("Scilab", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createScilab(), number);
					popup.setVisible(false);
				}
			}));
			popup.add(MyPopupMenu.createMenuItem("Histogram", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createHistogram(), number);
					popup.setVisible(false);
				}
			}));
			popup.add(MyPopupMenu.createMenuItem("Zone Average", new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					addLabContainer(factory.createZoneAverage(), number);
					popup.setVisible(false);
				}
			}));
			
		}

		popup.show(this, (int)point.getX() - this.getLocationOnScreen().x, (int)point.getY() - this.getLocationOnScreen().y);
		
		String panelName = jPanel.getName();
		if (panelName.equals("MainPanel")) {
			number = columns.size();
		}
		else if (panelName.contains("Rack")) {
			number = Integer.valueOf(panelName.substring(panelName.length()-1)) - 1;
		}
		else {
			if (jPanel.getParent().getClass().equals(JPanel.class)) {
				panelName = jPanel.getParent().getName();
				number = Integer.valueOf(panelName.substring(panelName.length()-1)) - 1;
			}
		}
		
	}

	protected void createOsaPackage() {
		LabContainer file = factory.createMs9740aCsv();
		addLabContainer(file, 0, false);
		LabContainer noise = factory.createNoiseDetector();
		addLabContainer(noise, 0, false);
		LabContainer chart = factory.createChart();
		addLabContainer(chart, 1, false);
		
		
		noise.setInConnector(NoiseDetectorWrapper.IN_Y, file.getOutConnector(Ms9740aCsvWrapper.OUTPUTLEVEL));
		noise.setInConnector(NoiseDetectorWrapper.IN_X, file.getOutConnector(Ms9740aCsvWrapper.OUTPUTWAVELENGTH));

		chart.setInConnector(ChartWrapper.INPUT1_X, file.getOutConnector(Ms9740aCsvWrapper.OUTPUTWAVELENGTH));
		chart.setInConnector(ChartWrapper.INPUT1_Y, file.getOutConnector(Ms9740aCsvWrapper.OUTPUTLEVEL));	
		chart.setInConnector(ChartWrapper.MARKER_X, noise.getOutConnector(NoiseDetectorWrapper.OUT_NOISESTART));	
		chart.setInConnector(ChartWrapper.MARKER_Y, noise.getOutConnector(NoiseDetectorWrapper.OUT_NOISELEVEL));
		
		buildLayout(true);
		
	}
	
	protected void createOtdrPackage() {
//		LabContainer auto = factory.createAutoSetting();
//		addLabContainer(auto, 0, false);
		LabContainer otdrTx = factory.createOtdrTx();
		addLabContainer(otdrTx, 0, false);
		LabContainer otdrRx = factory.createOtdrRx();
		addLabContainer(otdrRx, 0, false);
		LabContainer noise = factory.createNoiseDetector();
		addLabContainer(noise, 0, false);
		LabContainer chart = factory.createChartEx();
		addLabContainer(chart, 1, false);
		
		((ChartSettingEx)chart.getSetting()).setyAxisType(YAxisType.dB);
		
//		otdrTx.setInConnector(OtdrSimulatorTxWrapper.IN_DISTANCE, auto.getOutConnector(AutoSettingWrapper.OUTPUT_DISTANCE));
//		otdrTx.setInConnector(OtdrSimulatorTxWrapper.IN_PULSE, auto.getOutConnector(AutoSettingWrapper.OUTPUT_PULSEWIDTH));
		otdrRx.setInConnector(OtdrSimulatorRxWrapper.INPUT_LOSS, otdrTx.getOutConnector(OtdrSimulatorTxWrapper.OUT_LOSS));
//		otdrRx.setInConnector(OtdrSimulatorRxWrapper.INPUT_ATT, auto.getOutConnector(AutoSettingWrapper.OUTPUT_ATT));
//		otdrRx.setInConnector(OtdrSimulatorRxWrapper.INPUT_BANDWIDTH, auto.getOutConnector(AutoSettingWrapper.OUTPUT_BANDWIDTH));
		otdrRx.setInConnector(OtdrSimulatorRxWrapper.INPUT_FREQUENCY, otdrTx.getOutConnector(OtdrSimulatorTxWrapper.OUT_FREQUENCY));
//		otdrRx.setInConnector(OtdrSimulatorRxWrapper.INPUT_AVERAGE, auto.getOutConnector(AutoSettingWrapper.OUTPUT_AVERAGE));
		chart.setInConnector(ChartExWrapper.INPUT1_Y, otdrRx.getOutConnector(OtdrSimulatorRxWrapper.OUTPUT));
		chart.setInConnector(ChartExWrapper.INPUT_X, otdrTx.getOutConnector(OtdrSimulatorTxWrapper.OUT_DISTANCE));
		chart.setInConnector(ChartExWrapper.MARKER_Y, noise.getOutConnector(NoiseDetectorWrapper.OUT_NOISELEVEL));
		chart.setInConnector(ChartExWrapper.MARKER_X, noise.getOutConnector(NoiseDetectorWrapper.OUT_NOISESTART));

//		auto.setInConnector(AutoSettingWrapper.INPUT_DISTANCE, otdrTx.getOutConnector(OtdrSimulatorTxWrapper.OUT_DISTANCE));	
//		auto.setInConnector(AutoSettingWrapper.INPUT_LOSS, otdrRx.getOutConnector(OtdrSimulatorRxWrapper.OUTPUT));
		
		noise.setInConnector(NoiseDetectorWrapper.IN_Y, otdrRx.getOutConnector(OtdrSimulatorRxWrapper.OUTPUT));
		noise.setInConnector(NoiseDetectorWrapper.IN_X, otdrTx.getOutConnector(OtdrSimulatorTxWrapper.OUT_DISTANCE));
		
		buildLayout(true);
		
	}

	private void createController() {
		controller = new Controller() {

			@Override
			public void onSave() {
				showSaveDialog();
			}

			@Override
			public void onOpen() {
				showOpenDialog();
			}

			@Override
			public void onClear() {
				clearLayout();
			}
		};
		ControllerPanel ret = new ControllerPanel(controller);
		JFrame frame = new JFrame();
		frame.setTitle("Toolbar");
		frame.getContentPane().add(ret);
		frame.setVisible(true);
		frame.setSize(new Dimension(400,150));
	}

	protected void showSaveDialog() {
		JFileChooser fileChooser = new JFileChooser();
		int selected = fileChooser.showSaveDialog(this);
		if (selected == JFileChooser.APPROVE_OPTION){
		  File file = fileChooser.getSelectedFile();
		  PersistentManager.getInstance().save(file.getAbsolutePath(), columns);
		  this.repaint();
		}else if (selected == JFileChooser.CANCEL_OPTION){
		}else if (selected == JFileChooser.ERROR_OPTION){
		}
		
	}

	protected void showOpenDialog() {
		JFileChooser fileChooser = new JFileChooser();
		int selected = fileChooser.showOpenDialog(this);
		if (selected == JFileChooser.APPROVE_OPTION){
		  File file = fileChooser.getSelectedFile();
		  columns = PersistentManager.getInstance().open(file.getAbsolutePath());
		  this.buildLayout(false);
		}else if (selected == JFileChooser.CANCEL_OPTION){
		}else if (selected == JFileChooser.ERROR_OPTION){
		}	
	}
	
	private void buildLayout(boolean waving) {
		mainPanel.removeAll();
		
		for (List<LabContainer> list : columns) {
			if (list.size() == 0)continue;
			
			final JPanel col = createNewColumnPanel();

			col.addComponentListener(new ComponentListener() {
				@Override
				public void componentHidden(ComponentEvent arg0) {
					resizePartition(col);
				}

				@Override
				public void componentMoved(ComponentEvent arg0) {
					resizePartition(col);
				}

				@Override
				public void componentResized(ComponentEvent arg0) {
					//System.out.println("col resized:" + col.getWidth());
					resizePartition(col);
				}

				private void resizePartition(final JPanel col) {
					for (int i = 0; i < col.getComponentCount(); i++) {
						if (col.getComponent(i).getName().equals("partition")) {
							col.getComponent(i).setSize(new Dimension(col.getWidth(), 4));
						}
					}
				}

				@Override
				public void componentShown(ComponentEvent arg0) {
					resizePartition(col);
				}
				
			});
			
			for (LabContainer p : list) {
				final JPanel partition = new JPanel();
				partition.setName("partition");
				partition.setSize(new Dimension(col.getWidth(), 3));
//				partition.setBackground(Color.red);
				col.add(partition);

				partition.addMouseListener(new MouseListener() {

					@Override
					public void mouseClicked(MouseEvent arg0) {
					}

					@Override
					public void mouseEntered(MouseEvent arg0) {
						if (isLabContainerDragged()) {
							partition.setBackground(Color.GRAY);
							partitionPanel = partition;
						}
					}

					@Override
					public void mouseExited(MouseEvent arg0) {
						partition.setBackground(mainPanel.getBackground());
						partitionPanel = null;
					}

					@Override
					public void mousePressed(MouseEvent arg0) {
						// TODO Auto-generated method stub
						
					}

					@Override
					public void mouseReleased(MouseEvent arg0) {

					}
					
				});
				addLabContainerToPanel(p, col);
				
			}
			col.repaint();
		}

		if (waving) {
			wirePainter.startWavingDrawAll();
		}
		else {
			mainPanel.updateUI();
			repaint();			
		}
	}

	private JPanel createNewColumnPanel() {
		JPanel panel = new JPanel();
		mainPanel.add(panel);
		panel.setLayout(new VerticalLayout(0, VerticalLayout.LEFT));
		String name = "Rack #" + mainPanel.getComponentCount();
		panel.setBorder(new TitledBorder(name));
		panel.setName(name);
		panel.addMouseListener(mouseListener);
		panel.setAutoscrolls(true);
		//col.setPreferredSize(new Dimension(300, 500));
		return panel;
	}

	/**
	 *  Creates generic UI Panel
	 *  @version 1.0
	 *  @param LabContainer container
	 */
	private CalculatorUiPanel createCalculatorUiPanel(LabContainer container) {
		return new CalculatorUiPanel(container, uiPanelListener);
	}

	/**
	 *  Creates UI Panel with customized setting panel
	 *  @version 1.0
	 *  @param LabContainer container
	 */
	private CalculatorUiPanel createCalculatorUiPanel(LabContainer container,
			final SettingPanelUI settingUI) {
				return new CalculatorUiPanel(container, uiPanelListener) {

					@Override
					protected SettingPanelUI getSettingPanelUI(
							LabContainer container) {
						return settingUI;
					}
					
				};
		
	}
	
	/**
	 *  Creates UI Panel with customized outlet panel
	 *  @version 1.0
	 *  @param LabContainer container
	 */
	private CalculatorUiPanel createCalculatorUiPanel(LabContainer container,
			final OutletPanelUI outletUI) {
				return new CalculatorUiPanel(container, uiPanelListener) {

					@Override
					protected OutletPanelUI getOutputPanelUI(
							LabContainer container, UiPanelListener listener) {
						return outletUI;
					}

					
				};
		
	}
	
	private List<LabContainer> createColumn() {
		List<LabContainer> newColumn = new ArrayList<LabContainer>();
		columns.add(newColumn);
		return newColumn;
	}

	private void addLabContainer(LabContainer container, int column) {
		addLabContainer(container, column, true);
	}
	
	private void addLabContainer(final LabContainer container, int column, boolean build) {
		while (this.columns.size() <= column) {
			this.createColumn();
		}
		this.columns.get(column).add(container);
		setVisible(true);
		if (build) {
			this.buildLayout(true);
		}
	}

	private void addLabContainerToPanel(final LabContainer container, Component c) {
		JPanel p = (JPanel)c;
		String name = container.getCalculatorName();
		if (name.equals("OtdrSimulatorTxWrapper")) {
			SettingPanelListener listener = new SettingPanelListener() {
				@Override
				public void onSet() {
					container.process();
				}	
			};
			OtdrSettingPanelUI ui = new OtdrSettingPanelUI(container.getSetting(), listener);
			p.add(createCalculatorUiPanel(container, ui));
		}
		else if (name.equals("OtdrSimulatorRxWrapper")) {
			SettingPanelListener listener = new SettingPanelListener() {
				@Override
				public void onSet() {
					container.process();
				}	
			};
			SequencerSettingPanelUI ui = new SequencerSettingPanelUI(container.getSetting(), listener);
			p.add(createCalculatorUiPanel(container, ui));
		}
		else if (name.equals("Ms9740aCsvWrapper")||name.equals("TextReaderWrapper")||name.equals("Sr4731Wrapper")) {
			SettingPanelListener listener = new SettingPanelListener() {
				@Override
				public void onSet() {
					container.process();
				}
			};
			FileChooserPanelUI ui = new FileChooserPanelUI(container.getSetting(), listener);
			p.add(createCalculatorUiPanel(container, ui));
		}
		else if (name.equals("AutoSettingWrapper")) {
			SettingPanelListener listener = new SettingPanelListener() {
				@Override
				public void onSet() {
					container.process();
				}
			};
			AutoSettingPanelUI ui = new AutoSettingPanelUI(container.getSetting(), listener) {
				/**
				 * 
				 */
				private static final long serialVersionUID = 1L;

				@Override
				public void onRun() {
					container.process();
				}
			};
			p.add(createCalculatorUiPanel(container, ui));
		}
		else if (name.equals("ChartWrapper")) {
			ChartOutputPanelUI ui = new ChartOutputPanelUI(container, uiPanelListener);
			p.add(createCalculatorUiPanel(container, ui));
		}
		else if (name.equals("ChartExWrapper")) {
			ChartOutputPanelExUI ui = new ChartOutputPanelExUI(container, uiPanelListener);
			p.add(createCalculatorUiPanel(container, ui));
		}
		else {
			p.add(this.createCalculatorUiPanel(container));
		}
		
		container.getUI().addMouseListener(new MouseListener() {

			@Override
			public void mouseClicked(MouseEvent e) {
				if(javax.swing.SwingUtilities.isRightMouseButton(e)){
					factory.setActiveContainer(container);
					showPopup(e.getLocationOnScreen(), (JPanel)e.getSource());
				}
				else {
					hidePopup();
				}
			}

			@Override
			public void mouseEntered(MouseEvent e) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void mouseExited(MouseEvent e) {

			}

			@Override
			public void mousePressed(MouseEvent e) {
				beginDraggingLabContainerUI(container, e);
			}

			@Override
			public void mouseReleased(MouseEvent e) {
				finishDraggingLabContainerUI();
				repaint();
			}
			
		});
		
		container.getUI().addMouseMotionListener(new MouseMotionListener() {

			@Override
			public void mouseDragged(MouseEvent arg0) {
				dragLabContainerUI(container, arg0);
				doScroll(arg0.getLocationOnScreen());
				repaint();
			}

			@Override
			public void mouseMoved(MouseEvent arg0) {
				// TODO Auto-generated method stub
				
			}
			
		});
	}

	public void clearLayout() {
		columns.clear();
		this.buildLayout(false);
	}

	private void dragLabContainerUI(final LabContainer container,
			MouseEvent arg0) {
		draggedContainerX = arg0.getX() - draggedContainerXoffset;
		draggedContainerY = arg0.getY() - draggedContainerYoffset;
		draggedContainerUI = container.getUI();
	}

	private void beginDraggingLabContainerUI(final LabContainer container,
			MouseEvent e) {
		draggedContainerXoffset = e.getX() - container.getUI().getParent().getX() - container.getUI().getX() + (int)scrollPane.getViewport().getViewPosition().getX();
		draggedContainerYoffset = e.getY() - container.getUI().getParent().getY() - container.getUI().getY() + (int)scrollPane.getViewport().getViewPosition().getY();
		dragLabContainerUI(container, e);
		repaint();
	}

	private void doScroll(Point point2) {
	//	System.out.println(scrollPane.getViewport().getViewPosition().getY() + ":" + point2.getY());

		if (point2.y > scrollPane.getLocationOnScreen().getY() + scrollPane.getHeight() - 10) {
			Point point = scrollPane.getViewport().getViewPosition();
			point.y += 10;
			scrollPane.getViewport().setViewPosition(point);
		}
		else if (point2.getY() < scrollPane.getLocationOnScreen().getY() + 10/* && scrollPane.getViewport().getViewPosition().getY()<=0*/) {
			Point point = scrollPane.getViewport().getViewPosition();
			point.y -= 10;
			scrollPane.getViewport().setViewPosition(point);
		}
		
		if (point2.x > scrollPane.getLocationOnScreen().getX() + scrollPane.getWidth() - 10) {
			Point point = scrollPane.getViewport().getViewPosition();
			point.x += 10;
			scrollPane.getViewport().setViewPosition(point);
		}
		else if (point2.getX() < scrollPane.getLocationOnScreen().getX() + 10/* && scrollPane.getViewport().getViewPosition().getY()<=0*/) {
			Point point = scrollPane.getViewport().getViewPosition();
			point.x -= 10;
			scrollPane.getViewport().setViewPosition(point);
		}
	}

	abstract class FindPanel {
		abstract protected void found(JPanel rack, int column, int row);
				
		public void find(JPanel target) {
			for (int i = 0; i < mainPanel.getComponentCount(); i++) {
				JPanel rack = (JPanel)mainPanel.getComponent(i);
				
				int jj = 0;
				for (int j = 0; j < rack.getComponentCount(); j++) {
					if (rack.getComponent(j).equals(target)) {
						found(rack, i, jj);
						break;
					}
					if (rack.getComponent(j).getName().equals(target.getName())) {
						jj++;
					}
				}
			}
		}
	};
	
	private void finishDraggingLabContainerUI() {	
		if (partitionPanel != null) {
			new FindPanel() {
				@Override
				protected void found(JPanel rack, int column, int row) {
					moveLabContainer(draggedContainerUI.getContainer(), column, row);
				}
			}.find(partitionPanel);
		}

		draggedContainerUI = null;
	}

	private void moveLabContainer(final LabContainer container, int col, int row) {
		new FindPanel() {
			@Override
			protected void found(JPanel rack, int column, int row) {
				columns.get(column).remove(container);
			}
		}.find(container.getUI());
		
		this.columns.get(col).add(row, container);
		buildLayout(true);
	}

}
